﻿using System;
using Microsoft.Hpc.Scheduler.Session;
using Client.AsianOptionsService;
using System.ServiceModel;
using System.Threading;
using System.IO;
using System.Collections.Generic;


namespace Client
{
    class Program
    {
        [Serializable]
        class Context
        {
            public int range;
            public int iteration;
        }

        class result
        {
            public double sumPrice;
            public double sumSquarePrice;
            public double average;
            public double min;
            public double max;
            public double stdDev;
            public double stdErr;
            public int count;

            public result()
            {
                sumPrice = 0;
                sumSquarePrice = 0;
                average = 0;
                min = double.MaxValue;
                max = double.MinValue;
                stdDev = 0;
                stdErr = 0;
                count = 0;
            }

            public void init()
            {
                sumPrice = 0;
                sumSquarePrice = 0;
                average = 0;
                min = double.MaxValue;
                max = double.MinValue;
                stdDev = 0;
                stdErr = 0;
                count = 0;
            }
        }

        class record
        {
            public int day;
            public double opening;
            public double highest;
            public double lowest;
            public double closing;
            public long volume;

            public record(string line)
            {
                string[] fields = line.Split(',');
                if (int.TryParse(fields[0], out day))
                {
                    opening = double.Parse(fields[1]);
                    highest = double.Parse(fields[2]);
                    lowest = double.Parse(fields[3]);
                    closing = double.Parse(fields[4]);
                    volume = long.Parse(fields[5]);
                }
                else
                {
                    Log("skip line");
                }
            }
        }

        static void Log(string msg)
        {
            File.AppendAllText(Path.Combine(Path.GetDirectoryName(Environment.CommandLine), Environment.GetEnvironmentVariable("CCP_JOBID") + "_output.txt"), msg + "\r\n");
        }

        const int TotalRuns = 5000000;//Modified

        const int ClientNum = 32;

        const int runs = TotalRuns / ClientNum;

        const int OptionsNum = 1;

        const int TypeNum = 3;

        const int periods = 1000;//Modified




        
        /*
         * 1 for AsianOption
         * 2 for KnockInOutOption
         * 3 for LookAheadOption
         */
        const int typeId = 2;//Modified

        const int dayNum = 247;//Modified

        const int daysPerYear = 245;

        const int brownApproxiMethod = 0;//0:binomial method,1:0-1uniform distrution to log guass distrution generator

        double interest = 0.044 - 0.0378;//Modified

        double initial = 0;

        double exercise = 50;//Modified

        double up = 0;

        double down = 0;

        double deltaT = (dayNum / (250.0 * (double)periods));//Modified

        double threshold = 42;//Modified

        bool isCall = false;//Modified

        bool isFixed = true;//Modified

        bool isUp = true;//Modified

        int isAverage = 1;
        /*
         *  For AsianOption & KnockInOut:
          * 1 is for ArithmeticAverageAsianOptions
          * 2 is for GeometricAverageAsianOptions
          * 0 is not average
         * For KnockInOut
         * 3 is Lookback
         */


        double var = 1;

        int otherChoice = 1;
        /* 
         * For KnockInOut
         * 1 is for knock-in
         * 2 is for knock-out
         * /
       



       /*
        * This function is used to read the data from the excel and compute the parameters such as 
        * initial, var, interest, up, down and so on.
        * Before return from this function, a log file will be printed.
        */
        private void readData(string path)
        {
            if (File.Exists(path))
            {
                using (StreamReader sr = new StreamReader(path))
                {
                    List<double> R = new List<double>();
                    record preRec = null;
                    double sumR = 0;
                    double avgR = 0;
                    double closing = 0;
                    double[] avgPrice = new double[5];
                    double[] maxPrice = new double[5];
                    double[] minPrice = new double[5];
                    double[] avgVol = new double[5];
                    double[] vars = new double[5];
                    for (int i = 0; i < 5; i++)
                    {
                        avgPrice[i] = 0;
                        maxPrice[i] = 0;
                        minPrice[i] = 9999;
                        avgVol[i] = 0;
                        vars[i] = 0;
                    }
                    while (sr.Peek() >= 0)
                    {
                        string line = sr.ReadLine();
                        record rec = new record(line);

                        if (rec.day != -1)
                        {
                            if (rec.day > 1)
                            {
                                double r = Math.Log(rec.closing / preRec.closing);
                                R.Add(r);
                                closing = rec.closing;
                                //sumR += r;
                            }
                            int index = (rec.day-1) / daysPerYear;
                            avgPrice[index] += rec.closing / daysPerYear;
                            maxPrice[index] = Math.Max(maxPrice[index], rec.highest);
                            if (rec.day != 0)
                                minPrice[index] = Math.Min(minPrice[index], rec.lowest);
                            avgVol[index] += rec.volume / daysPerYear;
                            Log(string.Format("reading record of day {0}, opening {1}, closing {2} volume {3}", rec.day, rec.opening, rec.closing, rec.volume));
                        }
                        preRec = new record(line);
                    }
                    initial = closing;
                    for (int j = 0; j < 1; j++)
                    {
                        sumR = 0;
                        for (int i = 1; i < daysPerYear; i++)
                            sumR += R[R.Count - i - j * daysPerYear];
                        avgR = sumR / daysPerYear;
                        for (int i = 1; i < daysPerYear; i++)
                            vars[4 - j] += Math.Pow(R[R.Count - i - j * daysPerYear] - avgR, 2);
                        vars[4 - j] = Math.Pow(vars[4 - j] / (252 - 1), 0.5) * Math.Pow(252, 0.5);
                    }
                    var = vars[4];
                    //var = Math.Pow(var / (252 - 1), 0.5) * Math.Pow(252, 0.5);
                    up = Math.Exp(var * Math.Pow(deltaT, 0.5));

                    down = 1 / up;
                    interest = Math.Exp(interest * deltaT);
                    Log(String.Format("initial:{0} var:{1} up:{2} down:{3} interest:{4}", initial, var, up, down, interest));
                    Log(String.Format("avgPrice:{0} {1} {2} {3} {4}", avgPrice[0], avgPrice[1], avgPrice[2], avgPrice[3], avgPrice[4]));
                    Log(String.Format("maxPrice:{0} {1} {2} {3} {4}", maxPrice[0], maxPrice[1], maxPrice[2], maxPrice[3], maxPrice[4]));
                    Log(String.Format("minPrice:{0} {1} {2} {3} {4}", minPrice[0], minPrice[1], minPrice[2], minPrice[3], minPrice[4]));
                    Log(String.Format("avgVol:{0} {1} {2} {3} {4}", avgVol[0], avgVol[1], avgVol[2], avgVol[3], avgVol[4]));
                    Log(String.Format("vars:{0} {1} {2} {3} {4}", vars[0], vars[1], vars[2], vars[3], vars[4]));
                    //down = 2 * interest - up;
                }
            }
            else
            {
                Log(string.Format("ERROR: Input file {0} doesn't exist.", path));
            }

            Log("Hello, world. from job " + Environment.GetEnvironmentVariable("CCP_JOBID"));
        }

        private void mapOptionsPrice(DurableSession session, BrokerClient<IService1> client, int optionsId)
        {
            #region Compute_Price_in_Parallel
           
            for (int clientId = 0; clientId < ClientNum; clientId++)
            {
                Log(string.Format("Sending Req Batch OptionsId {0}", optionsId));

                bool batch_succeed = false;
                int batch_retrycount = 0;

                while (!batch_succeed && batch_retrycount < 3)
                {
                    bool i_succeed = false;
                    int i_retrycount = 0;

                    while (!i_succeed && i_retrycount < 3)
                    {
                        try
                        {
                            Context ctx = new Context();
                       
                            ctx.iteration = clientId;

                            // Type = 0: ArithmeticAverageAsianOptions
                            // Type = 1: GeometricAverageAsianOptions
                            // Type = 2: Knock-in-up
                            // Type = 3: Knock-in-down
                            // Type = 4: Knock-out-up
                            // Type = 5: Knock-out-down
                            // Type = 6: FixedLookBack-up
                            // Type = 7: FixedLookBack-down
                            // Type = 8: FloatLookBack-up
                            // Type = 9: FloatLookBack-down
                           

                            ComputeOptionRequest request = new ComputeOptionRequest(typeId, dayNum, interest, initial, exercise, up, down, deltaT, threshold, isCall, isFixed, isUp, isAverage, otherChoice, var, brownApproxiMethod, runs, periods, clientId);
                            client.SendRequest<ComputeOptionRequest>(request, ctx);

                          
                            i_succeed = true;
                           
                            Console.Write(".");

                        }
                        catch (Exception)
                        {
                            // Populate the cell with an error message
                            Log("#SendErr#");
                            i_retrycount++;
                        }
                    }

                    if (!i_succeed)
                    {
                        Log("Session failed.");
                        session.Close();
                        return;
                    }

                    try
                    {
                        client.Flush();
                        Log(string.Format("Req Batch {0} Flushed.", clientId));
                        batch_succeed = true;
                    }
                    catch (Exception ex)
                    {
                        // Populate the cell with an error message
                        Log(string.Format("ClientFlush failed. {0}", ex.ToString()));
                        batch_retrycount++;
                    }

                    if (!batch_succeed)
                    {
                        Log("Session failed.");
                        session.Close();
                        return;
                    }

                }
            } 
            #endregion
        }

        private void reduceOptionsPrice(BrokerClient<IService1> client, int optionsId)
        {
            #region Collect_All_Distributed_Price

            result results = new result();
            
            #region Get_Response


            foreach (BrokerResponse<ComputeOptionResponse> response in client.GetResponses<ComputeOptionResponse>())
            {
                double price = response.Result.ComputeOptionResult;

                results.count++;
                results.min = Math.Min(results.min, price);
                results.max = Math.Max(results.max, price);

                results.sumPrice += price;
                results.sumSquarePrice += price * price;

                results.stdDev = Math.Sqrt(results.sumSquarePrice - results.sumPrice * results.sumPrice / results.count) / ((results.count == 1) ? 1 : results.count - 1);
                results.stdErr = results.stdDev / Math.Sqrt(results.count);

            }
                    
            #endregion
            Log(string.Format("Options: {0} Type: {1} Price: {2:0.0000} Min: {3:0.0000} Max: {4:0.0000} Standard Deviation: {5:0.0000} Standard Error: {6:0.0000} count: {7} intrest: {8} up: {9} down: {10} var: {11}",
optionsId, typeId, results.sumPrice / results.count, results.min, results.max, results.stdDev, results.stdErr, results.count, interest, 0, 0, 0));
            #endregion
        }



        static void Main(string[] args)
        {
            Program pg = new Program { };
            string[] path = new string[OptionsNum];// @"C:\contestdata\input.csv";

            SessionStartInfo info = new SessionStartInfo(Environment.GetEnvironmentVariable("CCP_SCHEDULER"), "Contest");
            info.JobTemplate = "Contest4SOA";
            Log(Path.Combine(Path.GetDirectoryName(Environment.CommandLine), Environment.GetEnvironmentVariable("CCP_JOBID") + "_output.txt"));
            Log("Start session.");


            using (DurableSession session = DurableSession.CreateSession(info))
            {

                Log(String.Format("Session Created. SessionId {0}", session.Id));
                Thread.Sleep(1000);
                Log(String.Format(" Sending Req..."));

                NetTcpBinding binding;
                binding = new NetTcpBinding(SecurityMode.Transport);

                using (BrokerClient<IService1> client = new BrokerClient<IService1>(session, new NetTcpBinding(SecurityMode.Transport)))
                {
                    path[0] = @"C:\ContestData\1028-B.csv";
  
                    for (int optionsId = 0; optionsId < OptionsNum; optionsId++)
                    {
                        pg.readData(path[optionsId]);
                        pg.mapOptionsPrice(session, client, optionsId);
                    }
                    client.EndRequests();
                    Log("Closing.");
                }

                using (BrokerClient<IService1> client = new BrokerClient<IService1>(session))
                {
                    for (int optionsId = 0; optionsId < OptionsNum; optionsId++)
                    {
                        pg.reduceOptionsPrice(client, optionsId);
                    }
                }
                session.Close();
            }
        }
    }
}





